#ifndef _FINDWINDOWS_H_
#define _FINDWINDOWS_H_

/*
    Another fine piece of code from Bill's Ye Olde Code Shoppe.  Thing of beauty, ain't it?

    Sample usage:
    
        VisiblePredicate vp;
        ProcessPredicate pp(pi.dwProcessId);
        TitlePredicate   tp(L"Microsoft Visual Studio Codename Orcas");
        HWND hwnd = ::FindDesiredWindow(pp, vp, tp);
*/

#include <windows.h>

template<typename P> class ChildWindowFinder
{
	HWND m_hwnd;
    P    m_predicate;

	static BOOL CALLBACK EnumChildWindowsProc (HWND hwnd, LPARAM lparam)
    {
        ChildWindowFinder * pcwf = reinterpret_cast<ChildWindowFinder*>(lparam);
        return pcwf->EnumChildWindowsProc(hwnd);
    }
	BOOL CALLBACK EnumChildWindowsProc (HWND hwnd)
	{
		if (false == m_predicate(hwnd))
			return TRUE; // not a match:  continue to next window

		m_hwnd = hwnd;
		return FALSE; // stop enuming
	}

public:
	ChildWindowFinder(const P& predicate) : m_hwnd(NULL), m_predicate(predicate) {}
	HWND Find(HWND hwndParent)
	{
        m_hwnd = NULL;
		EnumChildWindows (hwndParent, EnumChildWindowsProc, reinterpret_cast<LPARAM>(this)); // NOTE:  passing NULL for hwndParent is equivalent to calling EnumWindows, per MSDN
		return m_hwnd;
	}
};

template<typename P1, typename P2> class BinaryPredicate
{
    P1 m_p1;
    P2 m_p2;
public:
    BinaryPredicate(const P1& p1, const P2& p2) : m_p1(p1), m_p2(p2) {}
    BinaryPredicate(const BinaryPredicate& rhs) : m_p1(rhs.m_p1), m_p2(rhs.m_p2) {}
    bool operator()(HWND hwnd)
    {
        return m_p1(hwnd) && m_p2(hwnd);
    }
};

template<typename P> HWND FindDesiredChildWindow(HWND hwnd, const P& predicate)
{
    ChildWindowFinder<P> wf(predicate);
    return wf.Find(hwnd);
}
template<typename P1, typename P2> HWND FindDesiredChildWindow(HWND hwnd, const P1& p1, const P2& p2)
{
    return FindDesiredChildWindow(hwnd, BinaryPredicate<P1, P2>(p1, p2));
}
template<typename P1, typename P2, typename P3> HWND FindDesiredChildWindow(HWND hwnd, const P1& p1, const P2& p2, const P3& p3)
{
    return FindDesiredChildWindow(hwnd, BinaryPredicate<P1, P2>(p1, p2), p3);
}
template<typename P1, typename P2, typename P3, typename P4> HWND FindDesiredChildWindow(HWND hwnd, const P1& p1, const P2& p2, const P3& p3, const P4& p4)
{
    return FindDesiredChildWindow(hwnd, BinaryPredicate<P1, P2>(p1, p2), p3, p4);
}
template<typename P1, typename P2, typename P3, typename P4, typename P5> HWND FindDesiredChildWindow(HWND hwnd, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5)
{
    return FindDesiredChildWindow(hwnd, BinaryPredicate<P1, P2>(p1, p2), p3, p4, p5);
}

template<typename P> HWND FindDesiredWindow(const P& predicate)
{
    return FindDesiredChildWindow(NULL, predicate);
}
template<typename P1, typename P2> HWND FindDesiredWindow(const P1& p1, const P2& p2)
{
    return FindDesiredChildWindow(NULL, p1, p2);
}
template<typename P1, typename P2, typename P3> HWND FindDesiredWindow(const P1& p1, const P2& p2, const P3& p3)
{
    return FindDesiredChildWindow(NULL, p1, p2, p3);
}
template<typename P1, typename P2, typename P3, typename P4> HWND FindDesiredWindow(const P1& p1, const P2& p2, const P3& p3, const P4& p4)
{
    return FindDesiredChildWindow(NULL, p1, p2, p3, p4);
}
template<typename P1, typename P2, typename P3, typename P4, typename P5> HWND FindDesiredWindow(const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5)
{
    return FindDesiredChildWindow(NULL, p1, p2, p3, p4, p5);
}

class ProcessPredicate
{
    DWORD m_dwProcessId;
public:
    ProcessPredicate(DWORD dwProcessId) : m_dwProcessId(dwProcessId) {}
    ProcessPredicate(const ProcessPredicate& rhs) : m_dwProcessId(rhs.m_dwProcessId) {}
    bool operator()(HWND hwnd) const
    {
	    DWORD dwProcessId = NULL;
		GetWindowThreadProcessId (hwnd, &dwProcessId);
        return dwProcessId == m_dwProcessId;
    }
};

class VisiblePredicate
{
public:
    VisiblePredicate() {}
    VisiblePredicate(const VisiblePredicate&) {}
    bool operator()(HWND hwnd) const
    {
        return !!::IsWindowVisible(hwnd);
    }
};

class TitlePredicate
{
    const WCHAR * m_pszTitle;
public:
	TitlePredicate(const WCHAR * pszTitle) : m_pszTitle(pszTitle) {}
    TitlePredicate(const TitlePredicate& rhs) : m_pszTitle(rhs.m_pszTitle) {}
    bool operator()(HWND hwnd) const
    {   // Get the title text
        WCHAR szCaption[1024] = {0};
        SendMessage(hwnd, WM_GETTEXT, 1024, (LPARAM)szCaption);
        return 0 == wcscmp(szCaption, m_pszTitle);
	}
};

class EnabledPredicate
{
public:
    EnabledPredicate() {}
    EnabledPredicate(const EnabledPredicate&) {}
    bool operator()(HWND hwnd) const
    {
        return !!::IsWindowEnabled(hwnd);
    }
};

class DialogPredicate
{
public:
    DialogPredicate() {}
    DialogPredicate(const DialogPredicate&) {}
    bool operator()(HWND hwnd) const
    {
        WCHAR className[7];
        ::GetClassName(hwnd, className, 7);
        return 0 == lstrcmp(className, L"#32770");
    }
};

class ClassPredicate
{
	const CString m_className;
	const int m_maxCount;
public:
	ClassPredicate(const CString& className)
		: m_className(className)
		, m_maxCount(className.GetLength()+1)
	{}
	bool operator()(HWND hwnd) const
	{
		CString cs;
		::GetClassName(hwnd, cs.GetBuffer(m_maxCount+1), m_maxCount);
		cs.ReleaseBuffer();
		return cs == m_className;
	}
};

struct StaticPredicate : public ClassPredicate
{
    StaticPredicate() : ClassPredicate(L"Static") {}
};

class NthStaticText : public StaticPredicate
{
	const int m_total;
	int m_count;
public:
	NthStaticText(int n)
		: m_count(0)
		, m_total(n)
	{}
	bool operator()(HWND hwnd)
	{
		if (false == StaticPredicate::operator()(hwnd))
			return false;
		if (++m_count < m_total)
			return false;
		return true;
	}			
};

#endif // _FINDWINDOWS_H_
